/* Filename: adc.c
   Author: Chris Chester
   Date: 19/09/2012
   Notes: This code was adapted from code created by Andrew Nicholas
   
   Discription: This code is designed to extract data about the ADC of a PIC micro processor.
*/

#include <p30f3013.h>
#include "type.h"
#include <adc12.h>
#include "debug_led.h"

/* Define */
/* LED pins are declared here to reduce overhead of additional function calls */
#define   YELLOW_LED 	_LATB7
#define   GREEN_LED		_LATB9

#define   YELLOW_LED_DDR	_TRISB7
#define   GREEN_LED_DDR		_TRISB9

/* Microcontroller Configuration */
#define AUTOSTART_MODE 7
#define TWO_SAMPLES_PER_INTR 1
#define TACQ 31         //Set up Acquisition time (Tacq) for 31 Tad periods
#define TACQTOT 63		// Total aquasition time over both channels
#define CHANNELS_TO_SCAN 0x0042	//Set-up channels to scan to be AN1 and AN6

/* Function Prototypes */
void __attribute__((__interrupt__)) _ADCInterrupt(void);

/* Module Variables */
uint16_t ADC_Data1 = 0;
uint16_t ADC_Data2 = 0;

uint8_t data_received = 0;

/*****************************************************************************
** Function name:		adc_init
**
** Descriptions:		Used to configure A/D to scan and convert 2 input 
                        channels per interrupt. The A/D is set up for a total
                        sampling rate of 5.23KHz or 2.613KHz per channel. The 
                        internal counter in the A/D is used to provide 
                        Acquisition time delay. The input pins being scanned 
                        are AN1 and AN6. AN1 and AN6 are connected to 
                        channel 1 and channel 2 of the Biofeedback Impedance
                        Monitor respectively .
**
** Parameters:	    	None
** Returned value:		None
**
**  Modified: This code is modified from http://www.moderncontroltechnology.com
**                              /docs/DataSheets/Microchip/more_stuff/dsPIC30/
**                              Example%20Software/30F4011/A_to_D_Converter.c,
**            accessed 18 Nov 2011.
**
*****************************************************************************/
void adc_init()
{
     

        //ADCON1 Register
        //Set up A/D for Auto-Convert
        ADCON1bits.SSRC = AUTOSTART_MODE;
        //Enable Automatic Sampling
        ADCON1bits.ASAM = 1;
        //All other bits to their default state

        //ADCON2 Register
        //Set up A/D for interrupting after 2 samples get filled in the buffer
        ADCON2bits.SMPI = TWO_SAMPLES_PER_INTR;
        //Also, enable Channel scanning
        ADCON2bits.CSCNA = 1;
        //All other bits to their default state

        //ADCON3 Register
        
        //Disable internal RC oscillator Clock
        ADCON3bits.ADRC = 0;
        //Set up Acquisition time (Tacq)
        ADCON3bits.SAMC = TACQ;
        ADCON3bits.ADCS = TACQTOT;
        //Given that each conversion takes 14*Tad (=Tconv) periods,
        //Total Sample Time = Acquisition Time + Conversion Time
        // = (31 + 14)*Tad = 45*Tad periods
        // = 45 * 31.5 * Tcy = 1417.5*Tcy periods
        //At 26.67 MIPS, Tcy = 37.5 ns = Instruction Cycle Time
        //So Tsamp = Tacq + Tconv = 45*Tad(in this example)= 53.2 microseconds
        //So Fsamp = Sampling Rate ~= 18.81 KHz (~9.4 kHz for AN1 and AN6 each)
        //All other bits to their default state

        //ADCHS Register
        //When Channel scanning is enabled (ADCON2bits.CSCNA=1)
        //AND Alternate mux sampling is disabled (ADCON2bits.ALTS=0)
        //then ADCHS is a "don't care"
        ADCHS = 0x0000;

        //ADCSSL Register
        //Scan channels AN6, AN1 as part of scanning sequence
        ADCSSL = CHANNELS_TO_SCAN;

        //ADPCFG Register
        //Set up channels AN1, AN6 as analog inputs and leave rest as digital
        //Recall that we configured all A/D pins as digital when code execution
        //entered main() out of reset
        ADPCFGbits.PCFG1 = 0;
        ADPCFGbits.PCFG6 = 0;

        //Clear the A/D interrupt flag bit
        IFS0bits.ADIF = 0;

        //Set the A/D interrupt enable bit
        IEC0bits.ADIE = 1;

        //Turn on the A/D converter
        //This is typically done after configuring other registers
        ADCON1bits.ADON = 1;

}

/*****************************************************************************
** Function name:		adc_read_ch1
**
** Descriptions:		Channel 1 data from the ADC buffer.
**
** Parameters:		    None
** Returned value:		None
**
*****************************************************************************/
uint16_t adc_read_ch1(){

    uint16_t data;
    data = ADC_Data1;

	return ADC_Data1; // Read from the first position in the buffer
}

/*****************************************************************************
** Function name:		adc_read_ch2
**
** Descriptions:		Channel 1 data from the ADC buffer.
**
** Parameters:		    None
** Returned value:		None
**
*****************************************************************************/
uint16_t adc_read_ch2(){

    uint16_t data;
    data = ADC_Data2;

	return ADC_Data2; // Read from the first position in the buffer
}


/*****************************************************************************
** Function name:		adc_busy
**
** Descriptions:		Checks the DONE bit and returns if the ADC is in the
**						middle of a conversion.			
**
** Parameters:		    None
** Returned value:		None
**
*****************************************************************************/
uint8_t adc_busy(){
	return BusyADC12();
}

/*****************************************************************************
** Function name:		adc_close
**
** Descriptions:		Stop ADC conversion
**
** Parameters:		    None
** Returned value:		None
**
*****************************************************************************/
void adc_close(){
    CloseADC12();
}

/*****************************************************************************
** Function name:		adc_is_new_data
**
** Descriptions:		Check to see if an interrupt has occured
**
** Parameters:		    None
** Returned value:		new_data
**
*****************************************************************************/
uint8_t adc_is_new_data(){
   return data_received;
}

/*****************************************************************************
** Function name:		adc_set_new_data
**
** Descriptions:		
**
** Parameters:		    None
** Returned value:		new_data
**
*****************************************************************************/
void adc_set_data_received(uint8_t new_data){
   data_received = new_data ;
   
   // Start A/D conversion again
   ADCON1bits.ADON = 1;
}

/*****************************************************************************
** Function name:		_ISR _ADCInterrupt
**
** Descriptions:		ADC Interrupt Vector
**
** Parameters:		    None
** Returned value:		None
**
*****************************************************************************/
void _ISRFAST _ADCInterrupt(void){

   //Set the data from each ADC to each corresponding Channel.
    ADC_Data1 = ADCBUF0;
    ADC_Data2 = ADCBUF1;
    
    // Turn off the A/D converter until data is receieved
    ADCON1bits.ADON = 0;
    data_received = 1;
    
    IFS0bits.ADIF = 0;
}
